/*******************************************************************************
* Copyright (c) 2010, 2017 Broadcom Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* James Blackburn (Broadcom Corp.) - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.tests.concurrency;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ILock;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.tests.harness.TestBarrier;
import org.eclipse.swt.widgets.Display;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Test for an issue where a lock, held by the UI thread
* is released while the UI thread is actually performing work
* having acquired it...
*/
public class Bug_262032 extends TestCase {
ISchedulingRule identityRule = new ISchedulingRule() {
@Override
public boolean isConflicting(ISchedulingRule rule) {
return rule == this;
}
@Override
public boolean contains(ISchedulingRule rule) {
return rule == this;
}
};
public static Test suite() {
return new TestSuite(Bug_262032.class);
}
volatile boolean concurrentAccess = false;
/**
* Threads: UI(+asyncExec), j
* Locks: lock, IDRule
*
* j holds identity Rule
* ui tries to acquire rule => block and performs asyncMessages
* asyncExec run and acquire()s lock
* j then attempts to acquire lock.
*
* Deadlock manager believes that UI is waiting for IDrule while holding
* lock, and Job holds IDRule while attempting lock. Scheduling rules
* are never released by the Deadlock detector, so the lock is yielded!
*
* The expectation is that when threads are 'waiting' they're sat
* in the ordered lock acquire which can give the locks safely to whoever
* is deemed to need it. In this case that's not true as the UI
* is running an async exec.
*
* The result is concurrent running in a locked region.
*/
public void testBug262032() {
final ILock lock = Job.getJobManager().newLock();
final TestBarrier tb1 = new TestBarrier(-1);
// Job hols scheduling rule
Job j = new Job ("Deadlocking normal Job") {
@Override
protected IStatus run(IProgressMonitor monitor) {
tb1.setStatus(TestBarrier.STATUS_WAIT_FOR_START);
tb1.waitForStatus(TestBarrier.STATUS_RUNNING);
lock.acquire();
//test that we haven't both acquired the lock...
assertTrue(!concurrentAccess);
lock.release();
tb1.setStatus(TestBarrier.STATUS_WAIT_FOR_DONE);
return Status.OK_STATUS;
};
};
j.setRule(identityRule);
j.schedule();
// Wait for the job with scheduling rule to start
tb1.waitForStatus(TestBarrier.STATUS_WAIT_FOR_START);
// asyncExec job that wants the lock
Display.getDefault().asyncExec(() -> {
lock.acquire();
concurrentAccess = true;
tb1.setStatus(TestBarrier.STATUS_RUNNING);
// Sleep to test for concurrent access
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
/* don't care */}
concurrentAccess = false;
lock.release();
});
// This will block, but the UI will continue to service async requests...
Job.getJobManager().beginRule(identityRule, null);
Job.getJobManager().endRule(identityRule);
try {
j.join();
tb1.waitForStatus(TestBarrier.STATUS_WAIT_FOR_DONE);
assertEquals(Status.OK_STATUS, j.getResult());
} catch (InterruptedException e) {fail();}
}
}